package pl.edu.pw.polygen.modeler.client.object;

import java.util.ArrayList;
import java.util.List;

import org.vaadin.gwtgraphics.client.Line;
import org.vaadin.gwtgraphics.client.shape.Text;

import pl.edu.pw.polygen.modeler.client.graph.BaseModel;
import pl.edu.pw.polygen.modeler.client.graph.EdgeModel;
import pl.edu.pw.polygen.modeler.client.graph.EdgeType;
import pl.edu.pw.polygen.modeler.client.graph.GraphHandler;
import pl.edu.pw.polygen.modeler.client.graph.GraphModel;
import pl.edu.pw.polygen.modeler.client.graph.IPropertyChangeListener;
import pl.edu.pw.polygen.modeler.client.graph.VertexModel;
import pl.edu.pw.polygen.modeler.client.object.Vertex.VertexType;
import pl.edu.pw.polygen.modeler.client.object.listener.EmptyListener;
import pl.edu.pw.polygen.modeler.client.object.listener.IPolygenObjectListener;
import pl.edu.pw.polygen.modeler.client.object.listener.PolygenObjectListenerMultiton;
import pl.edu.pw.polygen.modeler.client.object.listener.SelectableControlListener;
import pl.edu.pw.polygen.modeler.client.object.listener.SelectableSegmentListener;
import pl.edu.pw.polygen.modeler.client.utils.Scale;
import pl.edu.pw.polygen.modeler.client.workspace.IWorkspace;

import com.google.gwt.event.dom.client.MouseDownEvent;
import com.google.gwt.event.dom.client.MouseDownHandler;
import com.google.gwt.event.dom.client.MouseOutEvent;
import com.google.gwt.event.dom.client.MouseOutHandler;
import com.google.gwt.event.dom.client.MouseOverEvent;
import com.google.gwt.event.dom.client.MouseOverHandler;
import com.google.gwt.event.dom.client.MouseUpEvent;
import com.google.gwt.event.dom.client.MouseUpHandler;

/**
 * @author borysfan
 * @version 0.0.1
 */
public class Splain extends Line implements IPolygenObject, IPropertyChangeListener{

	private VertexModel beginVertexModel;
	private VertexModel endVertexModel;
	private VertexModel beginControlVertexModel;
	private VertexModel endControlVertexModel;
	private EdgeModel edgeModel;
	private Text boundaryText;
	
	private Vertex beginVertex;	
	private Vertex endVertex;
	private Vertex beginControlVertex;
	private Vertex endControlVertex;
	private Line beginControlLine;
	private Line endControlLine;
	private List<SubSplain> subLines;
	

	/* proportion takes value from 0 to 1. It determines how long will be
	 * control element at the begining
	 */
	private static double proportion = 0.2;
	
	/**
	 * Default step of points calculation
	 */
	private double defaultStep = 0.05;

	/**
	 * Value of step that will be used in calculations.
	 */
	private double step = 0.05;
	private Scale scale;
	private List<IPolygenObject> polygenParents;
	private Splain instance;
	
	private IWorkspace context;
	private IPolygenObjectListener polygenObjectListener;
	private boolean selected = false;
	private boolean holded = false;
	
	/**
	 * x coordination of mouse position when it is clicked
	 */
	private int mouseClickedX;
	
	/**
	 * y coordination of mouse position when it is clicked
	 */
	private int mouseClickedY;
	
	public static String DEFAULT_COLOR = "black";
	public static String HOVER_COLOR = "green";
	public static String SELECTED_COLOR = "red";
	public static String CONTROL_COLOR ="blue";
	
	public static int DEFAULT_WIDTH = 1;
	public static int HOVER_WIDTH = 2;
	public static int SELECTED_WIDTH = 2;
	
	public Splain(int x1, int y1, int x2, int y2, IWorkspace context) {
		super(x1, y1, x2, y2);
		polygenParents = new ArrayList<IPolygenObject>();
		boundaryText = new Text(0,0, "");
		boundaryText.setFontSize(10);
		boundaryText.setVisible(context.getBoundariesVisibility());
		scale = Scale.getInstance();
		instance = this;
		super.setStrokeColor(DEFAULT_COLOR);
		this.context = context;
		this.initializeModel();
		endVertex.hold();
	}
	
	public Splain(Vertex beginVertex, int x2, int y2, IWorkspace context) {
		super(beginVertex.getX(), beginVertex.getY(), x2, y2);
		polygenParents = new ArrayList<IPolygenObject>();
		boundaryText = new Text(0,0, "");
		boundaryText.setFontSize(10);
		boundaryText.setVisible(context.getBoundariesVisibility());
		scale = Scale.getInstance();
		instance = this;
		super.setStrokeColor(DEFAULT_COLOR);
		this.context = context;
		this.initializeModel(beginVertex);
		endVertex.hold();
	}
	
	public Splain(Vertex bv, Vertex ev, Vertex cbv, Vertex cev, double edgeStep,int boundary, IWorkspace context){
		super(0,0,0,0);
		polygenParents = new ArrayList<IPolygenObject>();
		boundaryText = new Text(0,0, "");
		boundaryText.setFontSize(10);
		boundaryText.setVisible(context.getBoundariesVisibility());
		scale = Scale.getInstance();
		instance = this;
		super.setStrokeColor(DEFAULT_COLOR);
		this.context = context;
		
		this.beginVertex = bv;
		beginVertex.addPolygenParent(this);
		this.endVertex = ev;
		endVertex.addPolygenParent(this);
		this.beginControlVertex = cbv;
		this.endControlVertex = cev;
		this.beginVertexModel = (VertexModel)beginVertex.getModel();
		this.beginVertexModel.addPropertyChangeListener(this);
		this.endVertexModel = (VertexModel)endVertex.getModel();
		this.endVertexModel.addPropertyChangeListener(this);
		this.edgeModel = (EdgeModel) GraphHandler.getInstance(context.getId()).addEdge(beginVertexModel, endVertexModel, EdgeType.SPLAIN);
		this.edgeModel.setStep(edgeStep);
		this.edgeModel.setBoundary(boundary);
		
		int x1 = getX1();
		int y1 = getY1();
		int x2 = getX2();
		int y2 = getY2();
		//first control element
		this.beginControlVertex.setStrokeColor(CONTROL_COLOR);
		this.beginControlVertex.setPolygenObjectListener(PolygenObjectListenerMultiton.getInstance(SelectableControlListener.class));
		this.beginControlVertex.setFillColor(CONTROL_COLOR);
		this.beginControlVertexModel = (VertexModel)beginControlVertex.getModel();
		this.edgeModel.setControlA(beginControlVertexModel);
		beginControlLine = new Line(x1, y1, beginControlVertex.getX(), beginControlVertex.getY());
		beginControlLine.setStrokeColor(CONTROL_COLOR);
		
		//second control element
		
		this.endControlVertex.setStrokeColor(CONTROL_COLOR);
		this.endControlVertex.setPolygenObjectListener(PolygenObjectListenerMultiton.getInstance(SelectableControlListener.class));
		this.endControlVertex.setFillColor(CONTROL_COLOR);
		this.endControlVertexModel = (VertexModel)endControlVertex.getModel();
		this.edgeModel.setControlB(endControlVertexModel);
		this.endControlLine = new Line(x2, y2, endControlVertex.getX(), endControlVertex.getY());
		this.endControlLine.setStrokeColor(CONTROL_COLOR);
		//listeners
		this.beginControlVertexModel.addPropertyChangeListener(this);
		this.endControlVertexModel.addPropertyChangeListener(this);
		this.addControlElements();
		
		this.initSplain(step);
		switchEdge();
		this.initListeners();
		this.beginVertex.initialize();
		this.endVertex.initialize();
		this.initStates();
		
		if(subLines!=null){
			for(SubSplain subSplain : subLines){
				subSplain.add();
			}
		}
	}
	
	private void initializeModel(){
		beginVertex = new Vertex(getX1(), getY1(),context,VertexType.NORMAL_VERTEX);
		beginVertex.addPolygenParent(this);
		endVertex = new Vertex(getX2(), getY2(),context,VertexType.NORMAL_VERTEX);
		endVertex.addPolygenParent(this);
		beginVertexModel = (VertexModel)beginVertex.getModel();
		beginVertexModel.addPropertyChangeListener(this);
		endVertexModel = (VertexModel)endVertex.getModel();
		endVertexModel.addPropertyChangeListener(this);
		edgeModel = (EdgeModel) GraphHandler.getInstance(context.getId()).addEdge(beginVertexModel, endVertexModel, EdgeType.SPLAIN);
		edgeModel.setStep(step);
	}
	
	private void initializeModel(Vertex bv){
		beginVertex = bv;
		beginVertex.addPolygenParent(this);
		endVertex = new Vertex(getX2(), getY2(),context,VertexType.NORMAL_VERTEX);
		endVertex.addPolygenParent(this);
		beginVertexModel = (VertexModel)beginVertex.getModel();
		beginVertexModel.addPropertyChangeListener(this);
		endVertexModel = (VertexModel)endVertex.getModel();
		endVertexModel.addPropertyChangeListener(this);
		edgeModel = (EdgeModel) GraphHandler.getInstance(context.getId()).addEdge(beginVertexModel, endVertexModel, EdgeType.SPLAIN);
		edgeModel.setStep(step);
	}
	
	/**
	 * Prepare object's actions. Initialize ends of segment
	 */
	@Override
	public void initialize() {
		initializeControl();
		this.initSplain(step);
		switchEdge();
		this.initListeners();
		beginVertex.initialize();
		endVertex.initialize();
		this.initStates();
	}
	
	private void switchEdge(){
		this.divideSplain(step);
		this.setVisible(false);
		for(SubSplain subSplain : subLines){
			subSplain.initialize();
		}
	}
	
	private void initializeControl(){
		int x1 = getX1();
		int y1 = getY1();
		int x2 = getX2();
		int y2 = getY2();
		//first control element
		beginControlVertex = new Vertex((int) ((x2 - x1) * proportion + x1), (int) ((y2 - y1) * proportion+y1), context,VertexType.CONTROL_VERTEX);
		beginControlVertex.setStrokeColor(CONTROL_COLOR);
		beginControlVertex.setPolygenObjectListener(PolygenObjectListenerMultiton.getInstance(SelectableControlListener.class));
		beginControlVertex.setFillColor(CONTROL_COLOR);
		beginControlVertexModel = (VertexModel)beginControlVertex.getModel();
		edgeModel.setControlA(beginControlVertexModel);
		beginControlLine = new Line(x1, y1, beginControlVertex.getX(), beginControlVertex.getY());
		beginControlLine.setStrokeColor(CONTROL_COLOR);
		
		//second control element
		
		endControlVertex = new Vertex((int) ((x2 - x1) * (1-proportion)+x1), (int) ((y2 - y1) * (1-proportion)+y1),context,VertexType.CONTROL_VERTEX);
		endControlVertex.setStrokeColor(CONTROL_COLOR);
		endControlVertex.setPolygenObjectListener(PolygenObjectListenerMultiton.getInstance(SelectableControlListener.class));
		endControlVertex.setFillColor(CONTROL_COLOR);
		endControlVertexModel = (VertexModel)endControlVertex.getModel();
		edgeModel.setControlB(endControlVertexModel);
		endControlLine = new Line(x2, y2, endControlVertex.getX(), endControlVertex.getY());
		endControlLine.setStrokeColor(CONTROL_COLOR);
		//listeners
		beginControlVertexModel.addPropertyChangeListener(this);
		endControlVertexModel.addPropertyChangeListener(this);
	}
		
	private void initListeners(){
		setLocked();
		MouseActions mouseActions = new MouseActions();
		this.addMouseOutHandler(mouseActions);
		this.addMouseOverHandler(mouseActions);
		this.addMouseDownHandler(mouseActions);
		this.addMouseUpHandler(mouseActions);
	}
	
	private void initStates(){
		this.selected = false;
		this.holded = false;
	}
	
	@Override
	public void notifyChanges() {
		beginVertexModel =(VertexModel)beginVertex.getModel();
		endVertexModel = (VertexModel)endVertex.getModel();
		int x1 = scale.getViewXValue(beginVertexModel.getX());
		int y1 = scale.getViewYValue(beginVertexModel.getY());
		int x2 = scale.getViewXValue(endVertexModel.getX());
		int y2 = scale.getViewYValue(endVertexModel.getY());
		
		setX1(x1);
		setY1(y1);
		
		setX2(x2);
		setY2(y2);
		if(beginControlVertexModel!=null && endControlVertexModel!=null){
			int bx = scale.getViewXValue(beginControlVertexModel.getX());
			int by = scale.getViewYValue(beginControlVertexModel.getY());
			beginControlLine.setX1(x1);
			beginControlLine.setY1(y1);
			beginControlLine.setX2(bx);
			beginControlLine.setY2(by);
			
			int ex = scale.getViewXValue(endControlVertexModel.getX());
			int ey = scale.getViewYValue(endControlVertexModel.getY());
			endControlLine.setX1(x2);
			endControlLine.setY1(y2);
			endControlLine.setX2(ex);
			endControlLine.setY2(ey);
		}
		divideSplain(step);
		
		if(context.getBoundariesVisibility()){
			boundaryText.setVisible(true);
			int middleIndex = subLines.size()/2;
			SubSplain middleSubSplain = subLines.get(middleIndex);
			boundaryText.setX(middleSubSplain.getX1());
			boundaryText.setY(middleSubSplain.getY1()-5);
			if(edgeModel.getBoundary()!=0){
				boundaryText.setText("("+edgeModel.getBoundary()+")");
			}else{
				boundaryText.setText("");
			}
		}else{
			boundaryText.setVisible(false);
		}
	}
	
	/* draw method which is called when we make some actions on objec. ie: move,
	* change position of ends
	*/
	@Override
	public void draw() {
		if(beginControlVertex!=null){
			beginControlVertex.draw();
			endControlVertex.draw();
		}
		
		if (isHolded()) {
			this.setPosition(context.getMouseX(), context.getMouseY());
			context.showProperties(this);
		}
	}

	/**
	 * adds Segment object to canvas 
	 */
	@Override
	public void add() {
		this.addToCanvas();
		context.add(this);
		
		beginVertex.add();
		endVertex.add();
		//if inialized
		if(beginControlVertex!=null && endControlVertex!=null){
			this.addControlElements();
		}
		if(subLines!=null){
			for(SubSplain subSplain : subLines){
				subSplain.add();
			}
		}
	}
	
	private void addControlElements(){
		beginControlVertex.addToCanvas();
		context.add(beginControlVertex);
		context.add(endControlVertex);
		endControlVertex.addToCanvas();
		beginControlLine.setParent(null);
		this.context.getCanvas().insert(beginControlLine,0);
		endControlLine.setParent(null);
		this.context.getCanvas().insert(endControlLine,0);
		setVisibleControlPoints(false);
		
	}

	@Override
	public void addToCanvas() {
		this.setParent(null);
		context.getCanvas().insert(this, 0);
		context.getCanvas().add(boundaryText);
	}
	
	private void setVisibleControlPoints(boolean visible){
		this.endControlVertex.setVisible(visible);
		this.beginControlVertex.setVisible(visible);
		this.beginControlLine.setVisible(visible);
		this.endControlLine.setVisible(visible);
	}
	
	/**
	 * removes Segment object from canvas 
	 */
	@Override
	public void remove() {
		removeFromCanvas();
		context.remove(this);
		if(beginControlVertex!=null && endControlVertex!=null){
			this.removeControlElements();
		}
		if(subLines!=null){
			for(SubSplain subSplain : subLines){
				subSplain.remove();
			}
		}
	}
	
	@Override
	public void removeFromCanvas() {
		beginVertex.remove();
		endVertex.remove();
		context.getCanvas().remove(this);
		context.getCanvas().remove(boundaryText);
	}
	
	private void removeControlElements(){
		beginControlVertex.remove();
		endControlVertex.remove();
		this.context.getCanvas().remove(beginControlLine);
		this.context.getCanvas().remove(endControlLine);
	}

	@Override
	public void unselect() {
		context.removeFromSelected(this);
		setHolded(false);
		setSelected(false);
		
		for(SubSplain sub : this.subLines){
			sub.unselect();
		}
	}
	
	@Override
	public void select() {
		setSelected(true);
		setHolded(false);
		for(SubSplain sub : this.subLines){
			sub.select();
		}
		context.addToSelected(this);
	}

	@Override
	public void drop() {
		beginVertex.drop();
		endVertex.drop();
		context.removeFromHolded(this);
		setHolded(false);
		setSelected(false);
		for(SubSplain sub : this.subLines){
			sub.drop();
		}
	}
	
	@Override
	public void hold() {
		setHolded(true);
		setSelected(false);
		for(SubSplain sub : this.subLines){
			sub.hold();
		}
		mouseClickedX = context.getMouseX();
		mouseClickedY = context.getMouseY();
		context.addToHolded(this);
	}
	
	@Override
	public void over() {
		setStrokeColor(HOVER_COLOR);
		setStrokeWidth(HOVER_WIDTH);
	}

	@Override
	public void out() {
		setStrokeColor(DEFAULT_COLOR);
		setStrokeWidth(DEFAULT_WIDTH);
	}
	


	@Override
	public void merge(IPolygenObject sourceObject, IPolygenObject targetObject) {
		if((sourceObject instanceof Vertex) && (targetObject instanceof Vertex)){
			GraphModel graph = GraphHandler.getInstance(context.getId());
			graph.removeEdge(beginVertexModel,endVertexModel,EdgeType.SPLAIN);
			
			Vertex sourceVertex = (Vertex) sourceObject;
			Vertex targetVertex = (Vertex) targetObject;
			if(beginVertex==sourceVertex){
				beginVertex = targetVertex;
			}else if(endVertex == sourceVertex){
				endVertex = targetVertex;
			}
			
			beginVertexModel = (VertexModel)beginVertex.getModel();
			endVertexModel = (VertexModel)endVertex.getModel();
			edgeModel = (EdgeModel)graph.addEdge(beginVertexModel, endVertexModel, EdgeType.SPLAIN);
			edgeModel.setStep(step);
			edgeModel.setControlA(beginControlVertexModel);
			edgeModel.setControlB(endControlVertexModel);
		}
	}
	
	@Override
	public void setPolygenObjectListener(IPolygenObjectListener polygenObjectListener) {
		this.polygenObjectListener = polygenObjectListener;
	}

	@Override
	public void setSelectable() {
		this.setPolygenObjectListener(PolygenObjectListenerMultiton.getInstance(SelectableSegmentListener.class));
	}

	@Override
	public void setMergeable() {
		this.setPolygenObjectListener(PolygenObjectListenerMultiton.getInstance(EmptyListener.class));
	}
	
	@Override
	public void setLocked() {
		this.setPolygenObjectListener(PolygenObjectListenerMultiton.getInstance(EmptyListener.class));
	}

	@Override
	public IWorkspace getContext() {
		return context;
	}
	
	/**
	 * @param context
	 * set context to all objects in segment
	 */
	public void setContext(IWorkspace context) {
		this.context = context;
		this.beginVertex.setContext(context);
		this.endVertex.setContext(context);
	}
	

	@Override
	public void setPosition(int x, int y) {
		int dx = x - mouseClickedX;
		int dy = y - mouseClickedY;
		mouseClickedX = x;
		mouseClickedY = y;

		beginVertex.setPosition(this.beginVertex.getX() + dx, this.beginVertex.getY() + dy);
		endVertex.setPosition(this.endVertex.getX() + dx, this.endVertex.getY() + dy);
		beginControlVertex.setPosition(this.beginControlVertex.getX() + dx, this.beginControlVertex.getY() + dy);
		endControlVertex.setPosition(this.endControlVertex.getX() + dx, this.endControlVertex.getY() + dy);
	}
	

	@Override
	public void setModel(BaseModel model) {
		if(model instanceof EdgeModel){
			EdgeModel edgeModel = (EdgeModel)model;
			this.beginVertexModel = (VertexModel)edgeModel.getBegin();
			this.endVertexModel = (VertexModel)edgeModel.getEnd();
		}
	}

	@Override
	public BaseModel getModel() {
		return (BaseModel)beginVertexModel.getEdge(endVertexModel);
	}
	
	@Override
	public List<IPolygenObject> getPolygenParents() {
		return polygenParents;
	}

	@Override
	public void addPolygenParent(IPolygenObject polygenParent) {
		if(!polygenParents.contains(polygenParent))
			this.polygenParents.add(polygenParent);
	}
	
	@Override
	public void removePolygenParent(IPolygenObject polygenParent) {
		if(polygenParents.contains(polygenParent))
			this.polygenParents.remove(polygenParent);
	}
	
	@Override
	public void showBoundary() {
		this.boundaryText.setVisible(true);
	}

	@Override
	public void hideBoundary() {
		this.boundaryText.setVisible(false);
	}

	public boolean isSelected() {
		return selected;
	}

	public void setSelected(boolean selected) {
		this.selected = selected;
		this.setVisibleControlPoints(selected);
		
	}
	
	public boolean isHolded() {
		return holded;
	}

	public void setHolded(boolean holded) {
		this.holded = holded;
	}

	/**
	 * @return Vertex
	 * begin of Segment
	 */
	public Vertex getBeginVertex() {
		return beginVertex;
	}

	/**
	 * @param beginVertex
	 * set begin of segment. Called when we want to merge polygen objects
	 */
	public void setBeginVertex(Vertex beginVertex) {
		this.beginVertex.remove();
		this.beginVertex = beginVertex;
		this.setX1(beginVertex.getX());
		this.setY1(beginVertex.getY());
	}

	/** 
	 * @return Vertex
	 * end of Segment
	 */
	public Vertex getEndVertex() {
		return endVertex;
	}

	/**
	 * @param endVertex
	 * set end of segment. Called when we want to merge polygen objects
	 */
	public void setEndVertex(Vertex endVertex) {
		this.endVertex.remove();
		this.endVertex = endVertex;
		this.setX2(endVertex.getX());
		this.setY2(endVertex.getY());
	}
	
	public Vertex getEndControlVertex(){
		return endControlVertex;
	}
	
	public Vertex getBeginControlVertex(){
		return beginControlVertex;
	}
	
	public void setEndControlVertex(Vertex v){
		endControlVertex = v;
	}
	
	public void setBeginControlVertex(Vertex v){
		beginControlVertex = v;
	}


	public void setStrokeWidth(int width){
		if (this.subLines != null) {
			for (SubSplain splain : this.subLines)
				splain.setStrokeWidth(width);
		}
	}
	
	public void setStrokeColor(String color){
		if(this.subLines!=null){
			for(SubSplain splain : this.subLines)
				splain.setStrokeColor(color);
		}
	}
	
	/**
	 * Initialize subLines List. Divide straight line into pieces. Creates lines
	 * first and last time. It should be called only once.
	 * 
	 * @param step
	 */
	private void initSplain(double step) {
		this.subLines = new ArrayList<SubSplain>();
		double x1 = this.beginVertex.getX();
		double y1 = this.beginVertex.getY();
		for (double t = 0; t < 1; t += this.step) {
			double x2 = computeBezierX(t);
			double y2 = computeBezierY(t);
			SubSplain subLine = new SubSplain(x1, y1, x2, y2, this.context);
			x1 = x2;
			y1 = y2;
			subLine.setParent(this);
			subLines.add(subLine);
		}
		SubSplain end = new SubSplain(x1, y1,this.endVertex.getX() ,this.endVertex.getY() , this.context);
		end.setParent(this);
		subLines.add(end);
	}

	/**
	 * Compute coordinates of sublines. It is call when coordination has been changed.
	 * 
	 * @param step step of calculation
	 */
	private void divideSplain(double step) {
		double x1 = this.beginVertex.getX();
		double y1 = this.beginVertex.getY();
		double t = 0;
		for (SubSplain subLine : this.subLines) {
			double x2 = computeBezierX(t);
			double y2 = computeBezierY(t);
			subLine.setX1(x1);
			subLine.setY1(y1);
			subLine.setX2(x2);
			subLine.setY2(y2);
			x1 = x2;
			y1 = y2;
			t += step;
		}
		this.subLines.get(this.subLines.size() - 1).setX2(this.endVertex.getX());
		this.subLines.get(this.subLines.size() - 1).setY2(this.endVertex.getY());
	}
	
	/**
	 * Calculate x coordinate of indirect point.
	 * @param t parameter of Bezier formula
	 * @return x coordinate of indirect point
	 */
	private double computeBezierX(double t){
		double ax = beginVertex.getX();
		double bx = beginControlVertex.getX();
		double cx = endControlVertex.getX();
		double dx = endVertex.getX();
		double result = ax * (1 - t) * (1 - t) * (1 - t) + 
						3 * bx * t * (1 - t) * (1 - t) + 
						3 * cx * t * t * (1 - t) + 
						dx * t * t * t;
		return result;
	}
	
	/**
	 * Calculate y coordinate of indirect point
	 * @param t parameter of Bezier formula
	 * @return y coordinate of indiect point
	 */
	private double computeBezierY(double t){
		double ay = beginVertex.getY();
		double by = beginControlVertex.getY();
		double cy = endControlVertex.getY();
		double dy = endVertex.getY();
		double result = ay * (1 - t) * (1 - t) * (1 - t) + 
						3 * by * t * (1 - t) * (1 - t) + 
						3 * cy * t * t * (1 - t) + 
						dy * t * t * t;
		return result;
	}

	/**
	 * @author borysfan
	 * Implementation of mouse actions 
	 */
	private class MouseActions implements MouseOutHandler, MouseOverHandler,
			MouseDownHandler, MouseUpHandler {

		@Override
		public void onMouseDown(MouseDownEvent event) {
			polygenObjectListener.onMouseDown(event, instance);
		}

		@Override
		public void onMouseUp(MouseUpEvent event) {
			polygenObjectListener.onMouseUp(event, instance);
		}

		@Override
		public void onMouseOver(MouseOverEvent event) {
			polygenObjectListener.onMouseOver(event, instance);
		}

		@Override
		public void onMouseOut(MouseOutEvent event) {
			polygenObjectListener.onMouseOut(event, instance);
		}
	}

}
